#	Copyright 2012, 2012 Noel Warren

#    This file is part of MiniVistii.
#    MiniVistii is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    MiniVistii is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Lesser General Public License for more details.

#    You should have received a copy of the GNU Lesser General Public License
#    along with MiniVistii.  If not, see <http://www.gnu.org/licenses/>.

#This file is where all the core classes live.  They are all pretty self explanatory
#except perhaps the Bundle class.  Make sure you undertand what it does.  It's a key
#minivistii class.

#The Bundle class is used to efficiently access Sketchup attributes. Use the
#open(entity) method to get access the data and remember to save() it.  Of particular
#importance is the gather("foo") method which itterates through all the entities and
#sub entities gathering entities that meet the criteria that was passed as an
#argument. The result is the gathered entities alongside the relevant transformation
#relative to the original bundle's entity.  If you can think of a better way to 
#word this, go ahead.

class Bundle < Hash
	def initialize(entity, transformation = Geom::Transformation.new)
		@entity = entity 
	end
	def Bundle.open(entity)
		@entity = entity
		bundle = Bundle.new(entity)
		dictionary = entity.attribute_dictionary("minivistii", true)
		dictionary.each_pair {|key,value|
			bundle[key] = value
		}
		return bundle
	end
	
	def save()
		@entity.delete_attribute "minivistii"
		self.each_pair {|key,value|
			@entity.set_attribute("minivistii", key, value)
		}
	end
	
	def entities()
		if @entity.is_a?(Sketchup::ComponentInstance)
			return @entity.definition.entities 
		elsif (entity.is_a? Sketchup::Group) || (entity.is_a? Sketchup::Model)
			return @entity.entities
		end
	end
	
	def bounds(transformation)
		points = []
		bb = Geom::BoundingBox.new
		entities.each{|entity|
			if entity.is_a? Sketchup::Edge
				points.push entity.start.position.transform(transformation)
				points.push entity.end.position.transform(transformation)
			end
		}
		bb.add points
		return bb
	end
	
	def reprecussion?
		if self["execution"] == "reprecussion"
			return true
		else
			return false
		end
	end
	
	def gather(target, bundle = self, transformation = Geom::Transformation.new)
		result = []
		entity = bundle.entity
		if (entity.is_a? Sketchup::Group) ||
		   (entity.is_a? Sketchup::ComponentInstance) ||
		   (entity.is_a? Sketchup::Model)
			transformation = (transformation * bundle.entity.transformation) if bundle != self
			if target == bundle["type"] 
				result.push [bundle, transformation]
			elsif bundle.entity.is_a?(Sketchup::ComponentInstance) && target == Bundle.open(bundle.entity.definition)["type"]
				result.push [bundle, transformation]
			end
			bundle.entities.to_a.each{|ent|
				gathered = gather(target, Bundle.open(ent), transformation)
				if gathered != nil
					result += gathered
				end
			}
		end
		return result
	end
	
	attr_accessor :entity
end

class Assembly < Array
	def initialize(bundle)
		@name = "model"
	end
	
	def to_s
		output = ["-------------ASSEMBLY--------------"]
		each{|workpiece, transformation| output.push workpiece.to_s}
		return output
	end
	
	attr_accessor :name
end

class Workpiece < Array
	def initialize(bundle, transformation)
		@toolpaths = []
		@reprecussionToolpaths = []
		@holes = []
		@reprecussionHoles = []
		@stock = bundle.gather("stock")[0][0]
		@reprecussions = []
		@transformation = transformation
		@name = bundle.entity.definition.name
	end
	
	def bounds(transformationX)
		bb = stock.bounds
		points = []
		points.push bb.corner(0).transform(transformationX)
		points.push bb.corner(7).transform(transformationX)
		return (Geom::BoundingBox.new).add(points)
	end
	
	def affected_by?(subjectBundle, subjectTransformation)
		point = Geom::Point3d.new(0,0,0).transform(subjectTransformation)
		bb = bounds(transformation)		
		if bb.contains? point
			return true
		else
			return false
		end
	end
	
	def to_s
		output = ["Workpiece: (toolpaths=>" + toolpaths.length.to_s + "), " +
							 "(holes=>" + holes.length.to_s + ")"]
		output += stock.to_s
		toolpaths.each{|toolpath| output += toolpath.to_s}
		holes.each{|hole| output += hole.to_s}
		return output
	end
		
	def x; return Toolbox.normalize(stock.x); end
	def y; return Toolbox.normalize(stock.y); end
	def z; return Toolbox.normalize(stock.z); end
	
	attr_accessor :toolpaths
	attr_accessor :reprecussionToolpaths
	attr_accessor :holes
	attr_accessor :reprecussionHoles
	attr_accessor :transformation
	attr_accessor :stock
	attr_accessor :reprecussions
	attr_accessor :design
	attr_accessor :name
end

class Stock
	def initialize(bundle,transformation)
		@bounds = bundle.entity.bounds
		@x = bounds.width
		@y = bounds.height
		@z = bounds.depth
		@offsetX
	end
	
	def to_s
		output = ["Stock: (x=>" + x.to_s + "), " +
						 "(y=>" + y.to_s + "), " +
						 "(y=>" + z.to_s + "), "]
		return output
	end
	
	attr_accessor :x
	attr_accessor :y
	attr_accessor :z
	attr_accessor :bounds
end

class Toolpath < Array
	def initialize(bundle,transformation)
		@vector = Geom::Vector3d.new(0,0,-1).transform transformation
		@tool = bundle["tool"][0,1]
		@correction = bundle["correction"][0,1]
		vertices = bundle.entities.to_a.collect{|entity| entity.vertices}.flatten.uniq
		vertices.each{|vertex|
			if (bundle = Bundle.open(vertex)["indices"])
				bundle.each{|index|
					self[index] = vertex.position.transform transformation
				}
			end
		}
	end

	def to_s
		output = ["Toolpath: (tool=>" + @tool + "), " +
							"(vector=>" + @vector.to_s + "), " +
							"(correction=>" + @correction + "), points..." ]
		self.each{|point| output.push point}
		return output
	end
		
	def tool();return @tool;end
	def vector();return @vector;end
	def correction();return @correction;end
end

class Hole
	def initialize(bundle, transformation)
		@diameter = bundle["diameter"]
		@point = Geom::Point3d.new(0,0,0).transform transformation
		@vector = Geom::Vector3d.new(0,0,-1).transform transformation
	end
	
	def to_s
		output = ["Hole: (diameter=>" + @diameter.to_s + "), " +
							"(vector=>" + @vector.to_s + "), point..."]
		output.push point.to_s
		return output
	end
	
	def point();return @point;end
	def vector();return @vector;end
	def diameter();return @diameter;end
end

class Program < Array
	def initialize(workpiece, gcode, part, parts, face)
		@workpiece = workpiece
		@gcode = gcode
		@face = face
		@part = part
		@parts = parts
	end
	attr_accessor :path
	attr_accessor :workpiece
	attr_accessor :gcode
	attr_accessor :part
	attr_accessor :parts
	attr_accessor :face
end
puts "Concepts OK"